// const obj = {
//     name: '长者',
//     _time: 1,
//     aa: ''
//   };

const { isObject } = require("util");

//  Object.defineProperty(obj, 'aa', {
//      configurable: true,
//      get(){
//          return '1'
//      },
//      set(val){
//         return this.aa = val;
//      }
//  })

//  console.log(obj.name); // 'default: 1s'
//   console.log(obj.aa); // 'default: 1s'
//   obj.aa = 2; // 2
//   console.log(obj.aa); // 'default: 1s'

// const a  = {
//     name: '111',
//     info: {
//         age: 20
//     }
// }
// const b = 1;
// b = 2;
// const b = {x: 1};
// b.x = 2;

// console.log(b.x) // 2


// var a = new Map();

// a.set('name', 'xx');
// console.log(a);

// console.log(
// a.size
// );

// console.log(
// a.keys(),
// a.values(),
// a.entries(),
// );
// var b = {
//     name: 1,
//     age:2,
//     setAttribute(key, val){
//         this[key] = val;
//     }
// }

// a.forEach((value, key)=>{
//     b[key] = value
// }, b)

// console.log(b)



// var target = {name: 'xx', age: 12}

// let proxy = new Proxy(target, {
//     set:(target, key, value) => {
//         // if(key == 'name'){
//         //     Reflect.set(target,key, value+'dddd')
//         // }else{
//         //     Reflect.set(target,key, value+2)
//         // }
//         // Reflect.set(target,key, value)
//     },
//     get: (target, key, receiver) =>{
//         console.log(receiver, target, 111)
//         receiver[key] = target[key] + 'nn';
//         Reflect.get(target, key)
//     }
// })
// console.log(target.name,1);
// console.log(proxy.name,2);
// console.log(target);
// console.log(proxy);


// let a ={};
// console.log(a?.b);

// var c = {base: 2};

// var b = [1,2,3]

// Array.from(b, function(v, k){
//     console.log(this)
//     return v**this.base
// }, c)
// function f(v){
//     return v > this.age;
//   }
//   let person = {name: 'John', age: 20};
//   console.log([10, 12, 26, 15].find(f, person));


// let map = {
//     // do: function(n) {
//     //     return n * 2;
//     // }
//     base: 2
// }
// let arrayLike = [1, 2, 3];
// console.log(Array.from(arrayLike, function (n, k){
//     return this.base * n
// }, map)); // [2, 4, 6]


// function add(n){
//     if(n==1) return 1;
//     return n*add(n-1)
// }

// console.log(add(5))


// function f(n){
//     if(n <= 1){
//         return 1;
//     };
//     return f(n-1) + f(n-2)
// }

// console.log(f(100))

// function fn(n, point = 1, count =1){
//     if(n <= 1) return count;
//     return fn(n-1, count, point + count)
// }

// console.log(fn(100))


// var a = [1,2,3]
// a.push(4);
// a.shift()

// console.log(a);


// function fn(n){
//     if(n <= 2) return n;
//     return fn(n-1)+fn(n-2)
// }

// console.log(fn(8));



// var arr = [4, 5, 6, 1, 2, 3];
// function sort(arr){
//     if(arr.length < 2)return arr;
//     var len = arr.length;
//     for(var i = 0; i < len; i++){
//         var min = i;
//         for(var j = i; j < len-1; j++){
//             if(arr[min] > arr[j+1]){
//                 min = j+1
//             }
//         }
//         [arr[i], arr[min]] = [arr[min], arr[i]]
//     }
//     return arr;
// }
// arr = sort(arr);
// console.log(
// arr
// )


// var arr= [1,3,2,4];
// arr[1]= arr[3]
// console.log(arr)

// var arr = [3,6,5,4,2,1];
// function sort(arr){
//     var len = arr.length;
//     if(len <2) return arr;
//     var mid = Math.floor(len/2), 
//         left = arr.slice(0, mid),
//         right = arr.slice(mid)
//     return merge(sort(left), sort(right))
// }
// function merge(left, right){
//     var result = [];
//     while(left.length && right.length){
//         if(left[0] < right[0]){
//             result.push(left.shift())
//         }else{
//             result.push(right.shift())
//         }
//     }
//     return result.concat(left, right)
// }

// var arr = [3,6,5,4,2,1];
// function sort(arr){
//     var len = arr.length;
//     if(len < 2)return arr;
//     var base = arr[0],
//     left = [], right = [];
//     for(var i = 1; i < len; i++){
//         if(arr[i] < base){
//             left.push(arr[i])
//         }else{
//             right.push(arr[i])
//         }
//     }
//     return sort(left).concat(base, sort(right))
// }
// console.log(sort(arr));

// var arr = [1,2,3,4,5,6,7,8,9,10,11]
// // 递归
// function find(arr, value){
//     var len = arr.length;
//     if(len==1) return (arr[0] == value);
//     var mid = Math.floor(len/2);
//     console.log(arr);
//     if(arr[mid] == value) return true;
//     if(arr[mid] > value) return find(arr.slice(0, mid), value)
//     if(arr[mid] < value) return find(arr.slice(mid), value)
// }

// console.log(find(arr, 5));


// var items = [1,2,3,4,5];
// var it = items[Symbol.iterator]();
// var obj = {length: 2, 0: "zero", 1: "one"}
// for(let x of Array.from(obj)){
//   console.log(x)
// }
// console.log(

//     it.next(),
//     it.next(),
//     it.next(),
//     it.next(),
//     it.next(),
//     it.next(),

// )



// let obj = {
//     data: [ 'hello', 'world' ],
//     [Symbol.iterator]() {
//       const self = this;
//       let index = 0;
//       return {
//         next() {
//           if (index < self.data.length) {
//             return {
//               value: self.data[index++],
//               done: false
//             };
//           } else {
//             return { value: undefined, done: true };
//           }
//         }
//       };
//     }
//   };

//   for(var x of obj){
//       console.log(x);
//   }



// var  a = [1,2,3,4];
// for(var i  =0; i < a.length; i++){
//     if(i == 2){
//         // continue;
//         // break;
//         return
//     }
//     console.log(i, a[i]);
// }
// var i = 0;
// var  a = [1,2,3,4];
// while(i < a.length){
//     i++;
//     if(i == 1){continue}
//     console.log(i);
// }


// var obj = {
//     name: '123',
//     age: 20
// }

// for(var x in obj){
//     console.log(x);
//     if(x == 'name'){
//         return;
//     }
// }

// var  a = [1,2,3,4];

// for(var i of a){
//     if(i == 2){
//         return;
//     }
//     console.log(i);
// }


// var a = [1, 2, 3, 4];
// try{

// a.forEach((value, index, arr) => {
//     console.log(value, index, arr);
//     if(value==2){
//         // break;
//         continue;
//     }
//     console.log('nnn')
// })
// }catch(e){
//     console.log(e, 222)
// }


// var a = {name: '123', age: 20};
// console.log(Object.prototype.toString.call(Object.entries(a)))
// console.log(Object.entries(a))

// var a = new Map();
// a.set('name', 123);
// a.set('age', 20);
// console.log(Object.prototype.toString.call(a.keys()))
// console.log(a.keys())
// var b  = a.entries();
// console.log(Object.prototype.toString.call(a))
// // for(var i in b){
// //     // console.log(b.hasOwnProperty(i));
// //     console.log(i);
// // }

// for(var x of a.keys()){
//     console.log(x)
//     if(x == 'name'){
//         break;
//     }
// }
// for(var x in a){
//     console.log(x);
// }

// var a = [1,2,3]
// var b = a.reduce((base, cur, index, arr) =>{
//     console.log(base)
//     return '1111'

// }, [1])
// console.log(a, 111)
// console.log(b, 111)

// class Foo {
//     constructor(...args) {
//       this.args = args;
//     }
//     * [Symbol.iterator]() {
//       for (let arg of this.args) {
//         yield arg;
//       }
//     }
//   }

//   for (let x of new Foo('hello', 'world')) {
//     console.log(x);
//   }

// class Foo{
//     constructor(...args){
//         this.args = args;
//     }
//     *[Symbol.iterator](){
//         for(var arg of this.args ){
//             yield arg
//         }
//     }

// }
// for(var x of new Foo('11','22')){
//     console.log(x);
// }



var p1 = new Promise((resolve, reject) => {
    resolve(1);
})

// var p2 = new Promise((resolve, reject)=>{
//     const n = x + 2;
//     resolve(n);
// }).catch(err => {
//     console.log(err)
//     return 1
// })

// var p3 = new Promise((resolve, reject)=>{
//     reject(3);
// }).then((vale)=>{
//     console.log(vale)
// }).catch(reason =>{
//     console.log(reason, 'ssss')
//     return 'sss'
// })

// Promise.all([p1, p2, p3]).then((values)=>{
//     console.log(values)
// }).catch(err => {
//     console.log(err, 1)
// })

// var a = new Promise((resolve, reject)=>{
//     // return resolve(1);
// })
// console.log(a);
// var b = a.then(res=>{
//     // console.log(res)
//     // console.log(a);
//     // return 1;
// })
// console.log(b);
// b.then(data=>{
//     console.log(data)
// }).then(a =>{
//     var b = a+x;
// }).then(res=>{
//     console.log(res)
// }).catch(err =>{
//     console.log(err);
// })

// function getApi(params) {
//     return new Promise(resolve => {
//         setTimeout(() => {
//             resolve('api result:' + params)
//         }, 3000);
//     })
// }


// function* aa() {
//     yield getApi('1111')
//     yield getApi('2222')
//     yield getApi('3333')
//     return 'end'
// }

// function run(gen, v) {
//     let { value, done } = gen.next();
//     if (!done) {
//         value.then(res => {
//             console.log(value, 11)
//             run(gen, res)
//         })
//     } else {
//         console.log(value);
//     }
// }
// var x = aa();
// // x.next();
// run(x)



// var arr = [1,(()=>{
//     return new Promise(resolve =>{
//         setTimeout(()=>{
//             resolve('22222222')
//         },4000)
//     })
// })(),3,5]
// var x = arr[Symbol.iterator]();

// console.log(x.next());
// var b = x.next();
// console.log(b);
// b.value.then(res=>{
//     console.log(res)
// })
// console.log(x.next());
// console.log(x.next());

// function* foo() {
//     yield 1;
//     yield 2;
//     yield 3;
//     yield 4;
//     yield 5;
//     return 6;
//   }
  
//   for (let v of foo()) {
//     console.log(v);
//   }

// async function a(){
//     var b = await new Promise(resolve => resolve('foo'))
//     console.log(b);
// }
// a()

// document.getElementById('f').addEventListener('change', function (e) {
//     var blob = this.files[0];
//     const size = blob.size;
//     var start = 0;
//     while(start < size){
//         upload(blob.slice(start, start +20));
//         start += 20
//     }
// }, false);

// function upload(blob) {
//     console.log(blob)
//     var xhr = new XMLHttpRequest();
//     xhr.open('POST', 'http://localhost:3000/ajax', true);
//     xhr.withCredentials = true;
//     xhr.setRequestHeader('Content-Type', 'text/plain')
//     xhr.send(blob);
// }

// document.getElementById('f').addEventListener('change', function (e) {
//     var blob = this.files[0];
//     const reader = new FileReader();
//     reader.onload = function(){
//         console.log(reader.result)
//         var u8 = new Uint8Array(reader.result);
//         console.log(u8);
//         u8[0] = 22;
//         console.log(u8);
//         // upload(reader.result);
//     }

//     reader.readAsArrayBuffer(blob);
// })
// function upload(blob) {
//     console.log(blob)
//     var xhr = new XMLHttpRequest();
//     xhr.open('POST', 'http://localhost:3000/ajax', true);
//     xhr.withCredentials = true;
//     xhr.setRequestHeader('Content-Type', 'text/plain')
//     xhr.send(blob);
// }


// var ab = new ArrayBuffer(12);

// var u8 = new Uint8Array(ab);

// console.log(u8);
// u8[0] = 11;
// u8[2] = 22;
// u8[3] = 33;

// console.log(u8)

// console.log(u8[0])
// var dv = new DataView(ab);
// console.log(dv.getBigInt64(2))

// var a = document.getElementById('f');

// window.addEventListener('click', e=>{
//     // e.stopPropagation()
//     console.log('window')
//     return false;
// }, true)
// a.addEventListener('click', function(e){
//     e.preventDefault()
//     e.cancelBubble = true
//     console.log('button')
// })

// document.getElementById('f').addEventListener('change', function (e) {
//     var blob = this.files[0];
//     const size = blob.size;
//     var start = 0;
//     while(start < size){
//         upload(blob.slice(start, start +20));
//         start += 20
//     }
// }, false);


var a = {
    name: 123
}
var val = a.name;

Object.defineProperty(a, 'name', {
    enumerable: true,
    get(val){
        console.log('name 属性被读取')
        return this.name
    },
    set(newValue){
        console.log('name 属性被修改成：', newValue)
        this.name = newValue;
    }
})

console.log(a.name)
// a.name = '2222'



class Observer{  // 把一个对象的所有属性转换为可观察的对象
    constructor(value){
        this.value = value
        def(value, '__ob__', this ) // 
        if(Array.isArray(value)){
            // 
        }else{
            this.walk(value)
        }
    }
    walk(obj: Object){
        const keys = Object.keys(obj)
        for(let i = 0; i < keys.length; i++){
            defineReactive(obj, keys[i])
        }
    }
}


// function defineReactive(obj, key, val){
//     if(arguments.length === 2){
//         val = obj[key]
//     }

//     if(typeof val === 'object'){
//         new Observer(val)
//     }

//     Object.defineProperty(obj, key,{
//         enumerable: true,
//         configurable: true,
//         get(){
//             console.log(`${key}属性被读取`)
//             return val;
//         },
//         set(newValue){
//             if(val === newValue){
//                 return
//             }
//             console.log(`${key}属性被修改`)
//             val = newValue;
//         }
//     })
// }

let car = Observer({
    name: 'byd',
    price: 30000
})

class Dep{
    constructor(){
        this.subs = []
    }
    addSub(sub){
        this.subs.push(sub)
    }
    removeSub(sub){
        remove(this.subs, sub)
    }
    depend(){
        if(window.target){
            this.addSub(window.target)
        }
    }
    notify(){
        const subs = this.subs.slice();
        for(let i = 0, l = subs.length; i < l; i++){
            subs[i].update()
        }
    }
}

function remove(arr, item){
    if(arr.length){
        const index = arr.indexOf(item);
        if(index > -1){
            return arr.splice(index, 1)
        }
    }
}


// => 

function defineReactive(obj, key, val) {
    if(arguments.length === 2){
        val = obj[key];
    }
    if(typeof val === 'object'){
        new Observer(val)
    }

    const dep = new Dep() // 实例化一个依赖管理器，生成一个依赖管理数组dep

    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get(){
            dep.depend();  // 在getter中收集依赖
            return val;
        },
        set(newVal){
            if(val === newVal){
                return;
            }
            val = newVal;
            dep.notify()   // 在setter中通知依赖更新
        }
    })
}

//  wahter 

class Watcher {
    constructor(vm, expOrFn, cb){
        this.vm = vm;
        this.cb = cb;
        this.getter = parsePath(expOrFn);
        this.value = this.get()
    }
    get(){
        window.target = this;
        const vm = this.vm;
        let value = this.getter.call(vm, vm);
        window.target = undefined;
        return value;
    }
    update(){
        const oldValue = this.value;
        this.value = this.get();
        this.cb.call(this.vm, this.value, oldValue)
    }
}
/**
 * Parse simple path.
 * 把一个形如'data.a.b.c'的字符串路径所表示的值，从真实的data对象中取出来
 * 例如：
 * data = {a:{b:{c:2}}}
 * parsePath('a.b.c')(data)  // 2
 */
const bailRE = /[^\w.$]/
function parsePath(path){
    if(bailRE.test(path)){
        return;
    }
    const segments = path.split('.');
    return function(obj){
        for(let i = 0; i < segments.length; i++){
            if(!obj) return;
            obj = obj[segments[i]]
        }
        return obj
    }
}

// 数组拦截器
const arrayProto = Array.prototype
// 创建一个对象作为拦截器
const arrayMethods = Object.create(arrayProto);

const methodsToPatch = [
    'push',
    'pop',
    'shift',
    'unshift',
    'sort',
    'reverse',
    'splice',
]

methodsToPatch.forEach(method => {
    const origial = arrayProto[method] // 缓存原生方法
    Object.defineProperty(arrayMethods, method, {
        enumerable: true,
        configurable: true,
        writable: true,
        value: function mutator(...args){  // mutator 函数中可以做更新
            const reuslt = origial.apply(this, args); // 谁调用了这个方法
            return result
        }
    })
});


// 使用拦截器 要把拦截器挂载到数组实例与Array.prototype之间，这样拦截器才能生效
// 其实挂载不难，只需要吧数据的的 __proto__ 属性设置为拦截器arrayMethods即可
const arrayMethods = Object.create(Array.prototype);

const arrayKeys = Object.getOwnPropertyNames(arrayMethods); // 自己的包括不可枚举的， 但是不包括 symbol值的
const hasProto = '__proto__' in {};
function protoAugment(target: Object, src: Object, keys: any ){
    target.__proto__ = src; // 修改它的原型指向，变成做了拦截的 arrayMethonds
}

function copyAugment(target: Object, src: Object, keys: any){
    for(let i = 0, l = keys.length; i < l; i++){
        const key = keys[i];
        // target[key] = src[key];
        defineReactive(taget, key, src[key]);  // 给taget 挂载所有的方法
    }
}
class Observer{
    constructor(value){
        this.value = value;
        if(Array.isArray(value)){
            const augment = hasProto ? protoAugment : copyAugment;
            augment(value, arrayMethods, arrayKeys)
        }else{
            this.walk(value)
        }
    }
}

// 依赖收集 在Observer类中实例化一个依赖管理，用来收集数组依赖
class Observer{
    constructor(value){
        this.value = value;
        this.dep = new Dep();  // 实例化一个依赖管理

        if(Array.isArray(value)){ // array
            const augment = hasProto ? protoAugment : copyAugment;
            augment(value, arrayMethods, arrayKeys);
        }else{
            this.walk(value); // object
        }
    }
}

// 依赖管理定义在 Observer 中， 我们在getter中收集依赖，所以getter中必须访问到Observer 类中的依赖管理
function defineReactive(obj, key, val){
    let childOb = observe(obj); // 获取obj的
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get(){
            if(childOb){
                childOb.dep.depend()
            }
            return val;
        },
        set(newValue){
            if(val === newValue){
                return
            }
            val = newValue;
            dep.notify();
        }
    })
}
// 尝试为value创建一个Observer实例， 如果创建成功，直接返回新创建的 Observer 实例
// 如果已经存在实例直接返回
function observe(value, asRootData){
    if(!isObject(value) || value instanceof VNode){
        return
    }
    let ob;
    if(hasOwn(value, '__ob__') && value.__ob__ instanceof Observer){
        ob = value.__ob__;
    }else{
        ob = new Observer(value);
    }
    return ob
}

// 如何通知依赖 在拦截器里通知依赖 在拦截器里访问Observer 的dep 就能访问到依赖了

methodsToPatch.forEach(method =>{
    const origial = arrayMethods[method];
    defineReactive(arrayMethods, mehtod, function mutator(...args){
        const result = origial.apply(this, args)
        const ob = this.__ob__;
        ob.dep.notify();
        return result;
    })
})

// 深度侦测 数组中的对象变化也要侦测 循环添加就行了
// 通过调用observe函数将每一个元素都转化成可侦测的响应式数据。

class Observer {
    value: any,
    dep: Dep,
    constructor(value){
        this.value = value;
        this.dep = new Dep();
        if(Array.isArray(value)){
            const augment = hasProto ? protoAugment : copyAugment;
            augment(value, arrayMethods, arrayKeys);
            this.observeArray(value)
        }else{
            this.walk(value);
        }
    },
    observeArray(items: Array<any>){
        items.forEach(item => {
            observe(item)
        })
    }
}

// 数组新增元素的侦测： 拿到新增元素然后通过observe转变成可侦测的响应数据
// 新增方法有 push unshift splice

methodsToPatch.forEach(function(method){
    const origial = arrayMethods[method];
    defineReactive(arrayMethods, method, function mutator(...args){
        const result = origial.apply(this, args);
        const ob = this.__ob__;
        let inserted;
        switch(method){
            case 'push':
            case 'unshift':
                inserted = args;
                break;
            case 'splice':
                inserted = args.slice(2);
                break;
        }
        if(inserted) ob.observeArray(inserted)
        ob.dep.notify()
        return result;
    })
})


// 总结：首先我们分析了对于Array 数据类型也在 getter 中进行依赖收集，其次我们发现，当数组数据被访问时我们轻而易举的知道
// 但是被修改的时候很难知道，为了解决这个问题，我们创建了数组的方法拦截器，从而成功的将数组数据变成可观测
// vue还对数组中每一个元素及新增元素都进行了变化观测




var a = x=> y => console.log(1, x, y);
var b = x=> y => console.log(2, x, y);
var c = x=> y => {console.log(3); return 'sss'};



function compose(...funcs) {
    if (funcs.length === 0) {
      return arg => arg
    }
    if (funcs.length === 1) {
      return funcs[0]
    }
    var result = funcs.reduce((a, b) => (...args) => a(b(...args)))
    console.log(result);
    return result
  }

  var d = compose(a,b,c)(5)
console.log(d())